package com.sky.utils;



import io.minio.*;
import io.minio.errors.*;
import io.minio.messages.Item;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import java.util.Optional;

@Data
@Slf4j
public class MinioUtil {

    private MinioClient minioClient;
    private String endpoint;
    private String accessKey;
    private String secretKey;
    private String bucketName;

    public MinioUtil(String endpoint, String accessKey, String secretKey, String bucketName) {
        this.endpoint = endpoint;
        this.accessKey = accessKey;
        this.secretKey = secretKey;
        this.bucketName = bucketName;
        this.minioClient = MinioClient.builder()
                .endpoint(endpoint)
                .credentials(accessKey, secretKey)
                .build();
    }


    /**
     *
     * 上传File文件
     *
     * @param file
     * @return
     */
    public String uploadFile(File file, String moduleName) {
        MultipartFile multipartFile = null;
        try {
            multipartFile = FileUtil.fileToMultipartFile(file);
        } catch (IOException e) {
            log.error("文件转MultipartFile失败!");
        }
        return uploadMultipartFile(multipartFile, moduleName);
    }

    /**
     *
     * 上传multipartFile文件
     * @param file
     * @return
     */
    public String uploadFile(MultipartFile file, String moduleName) {
        return uploadMultipartFile(file, moduleName);
    }

    /**
     * 上传multipartFile文件
     *
     * @param file
     * @param moduleName 必须为"../.." 或者 ".."格式 水利的兄弟们重点注意一下这里 ————————————----------------------------
     * @return
     */
    private String uploadMultipartFile(MultipartFile file,String moduleName) {
        //判断bucket是否存在
        existBucket(minioClient, bucketName);
        //判断目录路径是否存在
        if (!doesFolderExist(minioClient,bucketName, moduleName + "/")) {
            try {
                createFolder(minioClient,bucketName, moduleName + "/");
            } catch (Exception e) {
                //throw new RuntimeException(e);
                log.error("创建目录失败!");
            }
        }
        String fileName = file.getOriginalFilename();

        String[] split = fileName.split("\\.");
        if (split.length > 1) {
            fileName = split[0] + "_" + System.currentTimeMillis() + "." + split[1];
        } else {
            fileName = fileName + System.currentTimeMillis();
        }
        //String objectName = moduleName + fileName;
        String objectName = fileName;
        if(!"".equals(moduleName) && null != moduleName){
            objectName = moduleName + "/" + fileName;
        }


        //log.info("objectName是路径，文件最终整合的路径的值为:{}", objectName);

        InputStream in = null;
        try {
            in = file.getInputStream();
            //此方法为动态获取contentType,可以直接预览
            String contentType = getContentTypeByFileName(fileName);
            //log.info("contentType的值为:{}", contentType);
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .stream(in, in.available(), -1)
                    //如果为流,默认不预览直接下载
                    //.contentType("application/octet-stream")
                    .contentType(contentType)
                    .build()
            );
        } catch (Exception e) {
            log.error("上传文件失败!");
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        String url=endpoint + "/" + bucketName + "/" + objectName;
        log.info("上传minIo后文件url的值为:{}", url);
        return url;
    }



    /**
     * 根据文件名称下载File类型的文件
     *
     * @param fileUrl
     * @return
     */
//    public MultipartFile downloadMultipartFileByUrl(String fileUrl) {
//
//        try {
//            String fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1);
//            InputStream inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
//            MultipartFile multipartFile = InputStreamConvertMultipartFileUtil.getMultipartFile(inputStream, fileName);
//            return multipartFile;
//        } catch (Exception e) {
//            log.error("文件下载失败：{}", e.getMessage());
//            //throw new BizException(ErrorCodeEnum.GET_FILE_DOWNLOAD_URL_FAIL);
//        }
//    }

    /**
     * 根据文件名称下载File类型的文件
     *
     * @param fileUrl
     * @return
     */
    public InputStream downloadFileByUrl(String fileUrl) {
        try {
            String fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1);
            InputStream inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
            return inputStream;

        } catch (Exception e) {
            log.error("文件下载失败：{}", e.getMessage());
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 断点下载
     *
     * @param fileName 文件名称
     * @return 流
     */
    public InputStream getObject(String fileName)
            throws Exception {
        // 获取文件的总大小
        StatObjectResponse stat = minioClient.statObject(
                StatObjectArgs.builder().bucket(bucketName).object(fileName).build());
        long fileSize = stat.size();

        // 获取文件内容
        return minioClient.getObject(
                GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
    }

//    /**
//     * 获取路径下文件列表
//     *
//     * @param prefix    文件夹名称，就比如“xxx/xxx”
//     * @param recursive 是否递归查找，如果是false,就模拟文件夹结构查找
//     * @return 二进制流
//     */
//    public Iterable<Result<Item>> listObjects(String prefix,
//                                              boolean recursive) {
//        prefix = prefix + "/";
//        return minioClient.listObjects(
//                ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(recursive).build());
//    }









    public Long getFileSize(String fileName) throws Exception {
        // 获取文件的元数据
        StatObjectResponse stat = minioClient.statObject(
                StatObjectArgs.builder().bucket(bucketName).object(fileName).build()
        );
        return stat.size();  // 获取文件大小
    }






    /**
     * description: 判断bucket是否存在，不存在则创建
     *
     * @return: void
     */
    private void existBucket(MinioClient minioClient,String bucketName) {
        try {
            boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (!exists) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 判断文件夹是否存在
     *
     * @param bucketName 存储桶
     * @param objectName 文件夹名称（去掉/）
     * @return true：存在
     */
    private boolean doesFolderExist(MinioClient minioClient, String bucketName, String objectName) {
        boolean exist = false;
        try {
            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs.builder().bucket(bucketName).prefix(objectName).recursive(false).build());
            for (Result<Item> result : results) {
                Item item = result.get();
                if (item.isDir() && objectName.equals(item.objectName())) {
                    exist = true;
                }
            }
        } catch (Exception e) {
            exist = false;
        }
        return exist;
    }

    /**
     * 创建文件夹或目录
     *
     * @param bucketName 存储桶
     * @param objectName 目录路径
     */
    private ObjectWriteResponse createFolder(MinioClient minioClient,String bucketName, String objectName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        return minioClient.putObject(
                PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(
                                new ByteArrayInputStream(new byte[]{}), 0, -1)
                        .build());
    }

    /**
     * 、
     * 根据文件后缀名获取contentType
     */
    public static String getContentTypeByFileName(String fileName) {
        //此方法可以替代spring中根据文件后缀名获取contentType
        Optional<MediaType> mediaType = MediaTypeFactory.getMediaType(fileName);
        String contentType = mediaType.map(MediaType::toString).orElseGet(() -> MediaType.APPLICATION_OCTET_STREAM.toString());
        return contentType;
    }





}
